﻿using System;
using System.Collections.Generic;
using System.Text;
using Pixysoft.Framework.XSchEmA.Entity;

namespace Pixysoft.Framework.XSchEmA.Modifier
{
    class RealColumnModifier : IColumnModifier
    {
        ISchemaCommand command = null;

        ISchemaAspect aspect = null;

        public RealColumnModifier(ISchemaCommand command, ISchemaAspect aspect)
        {
            this.command = command;

            this.aspect = aspect;
        }


        /// <summary>
        /// 不支持false 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="fk"></param>
        /// <returns></returns>
        public bool CreateForeignKey(DatabaseSchema database, ForeignKeySchema fk)
        {
            command.SQL = aspect.SqlBuilder.AlterTableCreateConstraint(fk);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            new ColumnModifier(aspect).CreateForeignKey(database, fk);

            return true;
        }

        /// <summary>
        /// 不支持false 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="fk"></param>
        /// <returns></returns>
        public bool DeleteForeignKey(DatabaseSchema database, ForeignKeySchema fk)
        {
            command.SQL = aspect.SqlBuilder.AlterTableDropConstraint(fk);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).DeleteForeignKey(database, fk))
                return false;

            return true;
        }

        /// <summary>
        /// 不支持抛出 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public ForeignKeySchema[] DeleteForeignKeys(DatabaseSchema database, string tableName)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new Exception("missing tablename");

            TableSchema table = database.GetTable(tableName);

            if (table == null)
                throw new Exception("missing tablename");

            //由于没有实际操作database 因此需要记录已经删除的外键 否则自引用会重复

            List<ForeignKeySchema> fks = new List<ForeignKeySchema>();

            foreach (ForeignKeySchema fk in database.GetForeignKeys(table))
            {
                if (fks.Contains(fk))
                    continue;

                fks.Add(fk);

                command.SQL = aspect.SqlBuilder.AlterTableDropConstraint(fk);

                if (string.IsNullOrEmpty(command.SQL))
                    throw new NotSupportedException("do not support constraint creation.");

                command.Execute();
            }

            foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(table))
            {
                if (fks.Contains(fk))
                    continue;

                fks.Add(fk);

                command.SQL = aspect.SqlBuilder.AlterTableDropConstraint(fk);

                if (string.IsNullOrEmpty(command.SQL))
                    throw new NotSupportedException("do not support constraint creation.");

                command.Execute();
            }

            return new ColumnModifier(aspect).DeleteForeignKeys(database, tableName);
        }


        /// <summary>
        /// 不支持抛出 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="pkName"></param>
        /// <param name="table"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public PrimaryKeySchema CreatePrimaryKey(DatabaseSchema database, string pkName, TableSchema table, List<ColumnSchema> columns)
        {
            //TODO 删除unique，外键指向这个pk

            PrimaryKeySchema pk = new ColumnModifier(aspect).CreatePrimaryKey(database, pkName, table, columns);

            command.SQL = aspect.SqlBuilder.AlterTableCreateConstraint(pk);

            if (string.IsNullOrEmpty(command.SQL))
            {
                new ColumnModifier(aspect).DeletePrimaryKey(database, pk);

                throw new NotSupportedException("not support sql.");
            }

            command.Execute();

            return pk;
        }

        /// <summary>
        /// 不支持false  异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="pk"></param>
        /// <returns></returns>
        public bool CreatePrimaryKey(DatabaseSchema database, PrimaryKeySchema pk)
        {
            command.SQL = aspect.SqlBuilder.AlterTableCreateConstraint(pk);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).CreatePrimaryKey(database, pk))
                return false;

            return true;
        }

        /// <summary>
        /// 不支持false  异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="pk"></param>
        /// <returns></returns>
        public bool DeletePrimaryKey(DatabaseSchema database, PrimaryKeySchema pk)
        {
            foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(pk.Columns))
                DeleteForeignKey(database, fk);

            command.SQL = aspect.SqlBuilder.AlterTableDropConstraint(pk);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).DeletePrimaryKey(database, pk))
                return false;

            return true;
        }


        /// <summary>
        /// 不支持抛出 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="constraintName"></param>
        /// <param name="table"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public UniqueSchema CreateUnique(DatabaseSchema database, string constraintName, TableSchema table, List<ColumnSchema> columns)
        {
            UniqueSchema unique = new ColumnModifier(aspect).CreateUnique(database, constraintName, table, columns);

            command.SQL = aspect.SqlBuilder.AlterTableCreateConstraint(unique);

            if (string.IsNullOrEmpty(command.SQL))
            {
                new ColumnModifier(aspect).DeleteUnique(database, unique);

                throw new NotSupportedException("not support sql.");
            }

            command.Execute();

            return unique;
        }

        /// <summary>
        /// 不支持false  异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="unique"></param>
        /// <returns></returns>
        public bool CreateUnique(DatabaseSchema database, UniqueSchema unique)
        {
            command.SQL = aspect.SqlBuilder.AlterTableCreateConstraint(unique);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).CreateUnique(database, unique))
                return false;

            return true;
        }

        /// <summary>
        /// 不支持false  异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="unique"></param>
        /// <returns></returns>
        public bool DeleteUnique(DatabaseSchema database, UniqueSchema unique)
        {
            foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(unique.Columns))
                DeleteForeignKey(database, fk);

            command.SQL = aspect.SqlBuilder.AlterTableDropConstraint(unique);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).DeleteUnique(database, unique))
                return false;

            return true;
        }


        /// <summary>
        /// 不支持抛出 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="constraintName"></param>
        /// <param name="table"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public IndexSchema CreateIndex(DatabaseSchema database, string constraintName, TableSchema table, List<ColumnSchema> columns)
        {
            IndexSchema index = new ColumnModifier(aspect).CreateIndex(database, constraintName, table, columns);

            command.SQL = aspect.SqlBuilder.AlterTableCreateIndex(index);

            if (string.IsNullOrEmpty(command.SQL))
            {
                new ColumnModifier(aspect).DeleteIndex(database, index);

                throw new NotSupportedException("not support sql.");
            }

            command.Execute();

            return index;
        }

        /// <summary>
        /// 不支持false  异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="unique"></param>
        /// <returns></returns>
        public bool CreateIndex(DatabaseSchema database, IndexSchema index)
        {
            command.SQL = aspect.SqlBuilder.AlterTableCreateIndex(index);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).CreateIndex(database, index))
                return false;

            return true;
        }

        /// <summary>
        /// 不支持false  异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="unique"></param>
        /// <returns></returns>
        public bool DeleteIndex(DatabaseSchema database, IndexSchema index)
        {
            foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(index.Columns))
                DeleteForeignKey(database, fk);

            command.SQL = aspect.SqlBuilder.AlterTableDropIndex(index);

            if (string.IsNullOrEmpty(command.SQL))
                return false;

            command.Execute();

            if (!new ColumnModifier(aspect).DeleteIndex(database, index))
                return false;

            return true;
        }


        public bool ChangeColumnName(DatabaseSchema database, TableSchema table, ColumnSchema column, string columnName)
        {
            string oldname = column.ColumnName;

            string newname = columnName;

            //DROP CONSTRAINT

            List<ForeignKeySchema> fks = new List<ForeignKeySchema>();
            foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(column))
            {
                if (!fks.Contains(fk))
                    fks.Add(fk);
            }
            foreach (ForeignKeySchema fk in database.GetForeignKeys(column))
            {
                if (!fks.Contains(fk))
                    fks.Add(fk);
            }

            foreach (ForeignKeySchema fk in fks)
                DeleteForeignKey(database, fk);

            List<UniqueSchema> uniques = database.GetUniques(column);

            foreach (UniqueSchema unique in uniques)
                DeleteUnique(database, unique);

            PrimaryKeySchema pk = database.GetPrimaryKey(table);

            if (pk != null && pk.ColumnNames.Contains(oldname))
                DeletePrimaryKey(database, pk);



            //CREATE COLUMN

            ColumnSchema newcolumn = column.Clone();
            newcolumn.ColumnName = newname;
            AddColumn(database, table, newcolumn);

            //CREATE CONSTRINT

            if (pk != null && pk.ColumnNames.Contains(oldname))//2010-05-21 BUG!! 需要判断是否影响了pk
            {
                pk.ColumnNames[pk.ColumnNames.IndexOf(oldname)] = newname;
                CreatePrimaryKey(database, pk);
            }

            foreach (UniqueSchema unique in uniques)
            {
                unique.ColumnNames[unique.ColumnNames.IndexOf(oldname)] = newname;
                CreateUnique(database, unique);
            }

            foreach (ForeignKeySchema fk in fks)
            {
                fk.FkColumnNames[fk.FkColumnNames.IndexOf(oldname)] = newname;
                CreateForeignKey(database, fk);
            }

            //DROP COLUMN

            DeleteColumn(database, column);

            return true;
        }

        public bool ChangeComment(ColumnSchema column, string comment)
        {
            new ColumnModifier(aspect).ChangeComment(column, comment);

            return true;
        }

        public bool ChangeDefaultValue(ColumnSchema column, string value)
        {
            return false;
        }

        public bool ChangeDataType(DatabaseSchema database, TableSchema table, ColumnSchema column, DataTypeSchema dataType)
        {
            DataTypeSchema olddatatype = column.DataType;

            List<ColumnSchema> columns = database.GetStrongForeignColumnChain(column);
            List<PrimaryKeySchema> pks = new List<PrimaryKeySchema>();
            List<ForeignKeySchema> fks = new List<ForeignKeySchema>();
            List<UniqueSchema> uniques = new List<UniqueSchema>();

            // DROP CONSTRAINT

            foreach (ColumnSchema refercolumn in columns)
            {
                foreach (UniqueSchema unique in database.GetUniques(refercolumn))
                    if (!uniques.Contains(unique))
                        uniques.Add(unique);

                PrimaryKeySchema pk = database.GetPrimaryKey(refercolumn.TableName);
                if (pk != null && pk.Columns.Contains(refercolumn) && !pks.Contains(pk))
                    pks.Add(pk);

                foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(refercolumn))
                {
                    if (!fks.Contains(fk))
                        fks.Add(fk);
                }
                foreach (ForeignKeySchema fk in database.GetForeignKeys(refercolumn))
                {
                    if (!fks.Contains(fk))
                        fks.Add(fk);
                }
            }

            foreach (ForeignKeySchema fk in fks)
                DeleteForeignKey(database, fk);

            foreach (UniqueSchema unique in uniques)
                DeleteUnique(database, unique);

            foreach (PrimaryKeySchema pk in pks)
                DeletePrimaryKey(database, pk);



            //ALTER COLUMN

            foreach (ColumnSchema refercolumn in columns)
            {
                refercolumn.DataType = dataType;

                command.SQL = aspect.SqlBuilder.AlterTableModifyColumn(refercolumn);

                if (string.IsNullOrEmpty(command.SQL))
                    return false;

                command.Execute();

            }


            //CREATE CONSTRINT

            foreach (PrimaryKeySchema pk in pks)
            {
                CreatePrimaryKey(database, pk);
            }

            foreach (UniqueSchema unique in uniques)
            {
                CreateUnique(database, unique);
            }

            foreach (ForeignKeySchema fk in fks)
            {
                CreateForeignKey(database, fk);
            }

            return true;

        }


        public ColumnSchema AddColumn(DatabaseSchema database, TableSchema table, string columnName)
        {
            ColumnSchema tocolumn = new ColumnModifier(aspect).AddColumn(database, table, columnName);

            if (tocolumn == null)
                return tocolumn;

            command.SQL = aspect.SqlBuilder.AlterTableCreateColumn(tocolumn);

            if (string.IsNullOrEmpty(command.SQL))
            {
                new ColumnModifier(aspect).DeleteColumn(database, tocolumn);

                return tocolumn;
            }

            command.Execute();

            return tocolumn;
        }

        /// <summary>
        /// 不支持false 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="table"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public bool AddColumn(DatabaseSchema database, TableSchema table, ColumnSchema column)
        {
            if (!new ColumnModifier(aspect).AddColumn(database, table, column))
                return false;

            ColumnSchema tocolumn = table.GetColumn(column.ColumnName);

            if (tocolumn == null)
                return false;

            command.SQL = aspect.SqlBuilder.AlterTableCreateColumn(tocolumn);

            if (string.IsNullOrEmpty(command.SQL))
            {
                new ColumnModifier(aspect).DeleteColumn(database, column);

                return false;
            }

            command.Execute();

            return true;
        }

        /// <summary>
        /// 不支持false 异常抛出
        /// </summary>
        /// <param name="database"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public bool DeleteColumn(DatabaseSchema database, ColumnSchema column)
        {
            string dropcolumnsql = aspect.SqlBuilder.AlterTableDropColumn(column);

            if (string.IsNullOrEmpty(dropcolumnsql))
                return false;

            //首先删除所有外键关系

            if (!RemoveConstraint(database, column))
                return false;

            command.SQL = dropcolumnsql;

            command.Execute();

            if (!new ColumnModifier(aspect).DeleteColumn(database, column))
                return false;

            return true;
        }

        private bool RemoveConstraint(DatabaseSchema database, ColumnSchema column)
        {
            foreach (ForeignKeySchema fk in database.GetForeignKeys(column))
            {
                if (!DeleteForeignKey(database, fk))
                    return false;
            }

            foreach (ForeignKeySchema fk in database.GetBeingForeignKeys(column))
            {
                if (!DeleteForeignKey(database, fk))
                    return false;
            }

            foreach (UniqueSchema unique in database.GetUniques(column))
            {
                if (!DeleteUnique(database, unique))
                    return false;
            }

            foreach (IndexSchema index in database.GetIndexs(column))
            {
                if (!DeleteIndex(database, index))
                    return false;
            }

            if (!database.IsPrimaryKey(column))
                return true;

            PrimaryKeySchema pk = database.GetPrimaryKey(column.TableName, column.ColumnName);
            if (pk != null)
            {
                if (!DeletePrimaryKey(database, pk))
                    return false;
            }

            return true;
        }
    }
}
